package com.atguigu.yygh.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.common.exception.YyghException;
import com.atguigu.yygh.common.utils.JwtHelper;
import com.atguigu.yygh.enums.AuthStatusEnum;
import com.atguigu.yygh.model.user.Patient;
import com.atguigu.yygh.model.user.UserInfo;
import com.atguigu.yygh.user.mapper.UserInfoMapper;
import com.atguigu.yygh.user.service.PatientService;
import com.atguigu.yygh.user.service.UserInfoService;
import com.atguigu.yygh.vo.user.LoginVo;
import com.atguigu.yygh.vo.user.UserAuthVo;
import com.atguigu.yygh.vo.user.UserInfoQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author 孔德成
 * @slogan 致敬大师，致敬未来的你
 * @date 2021/9/8 7:13 上午
 * @desc
 */
@Service
@Slf4j
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    private final RedisTemplate<String, String> redisTemplate;
    private final PatientService patientService;

    public UserInfoServiceImpl(RedisTemplate<String, String> redisTemplate, PatientService patientService) {
        this.redisTemplate = redisTemplate;
        this.patientService = patientService;
    }

    /**
     * 1，登录采取弹出层的形式
     * 2，登录方式：
     * （1）手机号码+手机验证码
     * （2）微信扫描
     * 3，无注册界面，第一次登录根据手机号判断系统是否存在，如果不存在则自动注册
     * 4，微信扫描登录成功必须绑定手机号码，即：第一次扫描成功后绑定手机号，以后登录扫描直接登录成功
     * 5，网关统一判断登录状态，如何需要登录，页面弹出登录层
     *
     * @param loginVo 登陆参数
     * @return
     */
    @Override
    public Map<String, Object> phoneLogin(LoginVo loginVo) {
        // 1. 获取手机号
        String phone = loginVo.getPhone();
        // 2. 根据手机号查询数据库
        UserInfo userInfo = baseMapper.selectOne(new QueryWrapper<UserInfo>().eq("phone", phone));
        // 3. 如果用户不存在，则用户是首次登陆，将用户添加到数据库中
        if (ObjectUtil.isNull(userInfo)) {
            userInfo = new UserInfo();
            // 设置手机号
            userInfo.setPhone(phone);
            // 设置用户状态
            userInfo.setStatus(1);
            baseMapper.insert(userInfo);
        }
        // 4. 检查用户状态
        if (userInfo.getStatus() == 0) {
            throw new YyghException(20001, "账号已被锁定，请联系管理员");
        }
        // 5. 登陆成功，返回用户名和token
        String name = getUserName(userInfo);
        String token = JwtHelper.createToken(userInfo.getId(), name);
        HashMap<String, Object> map = new HashMap<>(2);
        map.put("name", name);
        map.put("token", token);
        return map;
    }

    @Override
    public Map<String, Object> wxLogin(JSONObject userInfoJson) {
        // 获取用户信息
        String openid = userInfoJson.getString("openid");
        String nickname = userInfoJson.getString("nickname");

        // 1. 根据 openid 查询数据库，看是否有对应记录
        UserInfo userQuery = baseMapper.selectOne(new QueryWrapper<UserInfo>().eq("openid", openid));
        Optional<UserInfo> optionalUser = Optional.ofNullable(userQuery);
        if (!optionalUser.isPresent()) {
            userQuery = new UserInfo();
            userQuery.setOpenid(openid);
            userQuery.setNickName(nickname);
            // 设置用户为未认证状态, 默认值为 0
            // userSave.setAuthStatus(0);
            // 设置用户为正常状态, 默认值为 1
            // userSave.setStatus(1);
            baseMapper.insert(userQuery);
        } else {
            userQuery.setNickName(nickname);
            baseMapper.updateById(userQuery);
        }
        Map<String, Object> param = userQuery.getParam();
        // 2. 检查是否绑定过手机号
        String phone = userQuery.getPhone();
        if (StrUtil.isBlank(phone)) {
            // 2.1 未绑定执行绑定手机号逻辑
            // 前台根据 openid 参数的有无来决定是否执行绑定手机号逻辑
            param.put("openid", openid);
        } else {
            // 2.2 绑定过手机号执行登陆逻辑
            // 获取用户名
            String userName = getUserName(userQuery);
            // 根据用户 id 和用户名生成 token
            String token = JwtHelper.createToken(userQuery.getId(), userName);

            param.put("name", userName);
            param.put("token", token);
            param.put("openid", "");
        }

        return param;
    }

    @Override
    public Map<String, Object> userLogin(LoginVo loginVo) {
        // 如果有 openid 参数，先走微信登陆绑定手机号逻辑，再走手机登陆逻辑
        if (StrUtil.isNotBlank(loginVo.getOpenid())) {
            this.wxBindPhoneNumber(loginVo);
        }
        return this.phoneLogin(loginVo);
    }


    @Override
    public boolean checkCode(String phone, String code) {
        String s = redisTemplate.opsForValue().get(phone);
        return StrUtil.equals(code, s);
    }

    @Transactional
    @Override
    public void wxBindPhoneNumber(LoginVo loginVo) {
        // 1. 查询微信账号
        UserInfo userWx = baseMapper.selectOne(new QueryWrapper<UserInfo>().eq("openid", loginVo.getOpenid()));
        // 2. 查询手机账号
        UserInfo userPhone = baseMapper.selectOne(new QueryWrapper<UserInfo>().eq("phone", loginVo.getPhone()));
        Optional<UserInfo> user = Optional.ofNullable(userPhone);
        // 3. 手机账号不为空，说明用户曾经注册过，执行合并操作
        if (user.isPresent()) {
            // 设置属性注入规则，忽略空值，忽略属性列表
            CopyOptions copyOptions = CopyOptions.create().setIgnoreNullValue(true).setIgnoreProperties("id", "createTime", "updateTime", "isDeleted", "nickName");
            // 将手机账号中的信息复制到微信账号中
            BeanUtil.copyProperties(userPhone, userWx, copyOptions);
            // 更新微信账号
            baseMapper.updateById(userWx);
            // 删除手机账号
            baseMapper.deleteById(userPhone.getId());
        } else {
            // 未注册过手机账号，则直接更新手机号即可
            userWx.setPhone(loginVo.getPhone());
            baseMapper.updateById(userWx);
        }
    }

    @Override
    public UserInfo getById(Long userId) {
        UserInfo userInfo = baseMapper.selectById(userId);
        // 封装实名认证状态字符串
        String statusNameByStatus = AuthStatusEnum.getStatusNameByStatus(userInfo.getAuthStatus());
        userInfo.getParam().put("authStatusString", statusNameByStatus);
        log.info(statusNameByStatus);
        return userInfo;
    }

    @Override
    public void userAuth(Long userId, UserAuthVo userAuthVo) {
        // 1. 查询用户
        UserInfo userInfo = baseMapper.selectById(userId);
        // 2. 将实名认证信息复制到 userInfo 对象中
        BeanUtil.copyProperties(userAuthVo, userInfo);
        // 3. 设置实名认证状态为认证中
        userInfo.setAuthStatus(AuthStatusEnum.AUTH_RUN.getStatus());
        // 4. 执行更新操作
        baseMapper.updateById(userInfo);
    }

    @Override
    public IPage<UserInfo> selectPage(Page<UserInfo> pageParam, UserInfoQueryVo userInfoQueryVo) {
        //UserInfoQueryVo获取条件值
        String name = userInfoQueryVo.getKeyword(); //用户名称
        Integer status = userInfoQueryVo.getStatus();//用户状态
        Integer authStatus = userInfoQueryVo.getAuthStatus(); //认证状态
        String createTimeBegin = userInfoQueryVo.getCreateTimeBegin(); //开始时间
        String createTimeEnd = userInfoQueryVo.getCreateTimeEnd(); //结束时间
        //对条件值进行非空判断
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        if (StrUtil.isNotBlank(name)) {
            wrapper.like("name", name);
        }
        if (ObjectUtil.isNotNull(status)) {
            wrapper.eq("status", status);
        }
        if (ObjectUtil.isNotNull(authStatus)) {
            wrapper.eq("auth_status", authStatus);
        }
        if (StrUtil.isNotBlank(createTimeBegin)) {
            wrapper.ge("create_time", createTimeBegin);
        }
        if (StrUtil.isNotBlank(createTimeEnd)) {
            wrapper.le("create_time", createTimeEnd);
        }
        //调用mapper的方法
        IPage<UserInfo> pages = baseMapper.selectPage(pageParam, wrapper);
        //编号变成对应值封装
        pages.getRecords().forEach(this::packageUserInfo);
        return pages;
    }

    //编号变成对应值封装
    private UserInfo packageUserInfo(UserInfo userInfo) {
        //处理认证状态编码
        userInfo.getParam().put("authStatusString", AuthStatusEnum.getStatusNameByStatus(userInfo.getAuthStatus()));
        //处理用户状态 0  1
        String statusString = userInfo.getStatus() == 0 ? "锁定" : "正常";
        userInfo.getParam().put("statusString", statusString);
        return userInfo;
    }

    private String getUserName(UserInfo userInfo) {
        // 1. 返回昵称
        if (StrUtil.isNotBlank(userInfo.getNickName())) {
            return userInfo.getNickName();
        }
        // 2. 返回姓名
        if (StrUtil.isNotBlank(userInfo.getName())) {
            return userInfo.getName();
        }
        // 3. 返回手机号
        return userInfo.getPhone();


    }


    @Override
    public void lock(Long userId, Integer status) {
        if (status == 0 || status == 1) {
            UserInfo userInfo = this.getById(userId);
            userInfo.setStatus(status);
            this.updateById(userInfo);
        }
    }

    @Override
    public Map<String, Object> show(Long userId) {
        Map<String, Object> map = new HashMap<>(2);
        //根据userid查询用户信息
        UserInfo userInfo = this.packageUserInfo(baseMapper.selectById(userId));
        map.put("userInfo", userInfo);
        //根据userid查询就诊人信息
        List<Patient> patientList = patientService.findAllUserId(userId);
        map.put("patientList", patientList);
        return map;
    }

    //认证审批  2通过  -1不通过
    @Override
    public void approval(Long userId, Integer authStatus) {
        if(authStatus ==2 || authStatus ==-1) {
            UserInfo userInfo = baseMapper.selectById(userId);
            userInfo.setAuthStatus(authStatus);
            baseMapper.updateById(userInfo);
        }
    }
}
